package org.loong.crypto.core.algorithm;

import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.loong.crypto.core.Algorithm;
import org.loong.crypto.core.AlgorithmFamily;

/**
 * The Encryption method.
 */
public final class EncryptionMethod extends Algorithm {

    /**
     * The encryption algorithm.
     */
    private String algorithm;

    /**
     * The encryption Key bit length, zero if not specified.
     */
    private final int keyLength;

    /**
     * Encryption using AES/ECB/NoPadding algorithm with key bitLength 128.
     */
    public static final EncryptionMethod AES128_ECB = new EncryptionMethod("AES-128/ECB", NISTObjectIdentifiers.id_aes128_ECB, "AES/ECB/NoPadding", 128);

    /**
     * Encryption using AES/ECB/NoPadding algorithm with key bitLength 192.
     */
    public static final EncryptionMethod AES192_ECB = new EncryptionMethod("AES-192/ECB", NISTObjectIdentifiers.id_aes192_ECB, "AES/ECB/NoPadding", 192);

    /**
     * Encryption using AES/ECB/NoPadding algorithm with key bitLength 256.
     */
    public static final EncryptionMethod AES256_ECB = new EncryptionMethod("AES-256/ECB", NISTObjectIdentifiers.id_aes256_ECB, "AES/ECB/NoPadding", 256);

    /**
     * Encryption using AES/CBC/NoPadding algorithm with key bitLength 128.
     */
    public static final EncryptionMethod AES128_CBC = new EncryptionMethod("AES-128/CBC", NISTObjectIdentifiers.id_aes128_CBC, "AES/CBC/NoPadding", 128);

    /**
     * Encryption using AES/CBC/NoPadding algorithm with key bitLength 192.
     */
    public static final EncryptionMethod AES192_CBC = new EncryptionMethod("AES-192/CBC", NISTObjectIdentifiers.id_aes192_CBC, "AES/CBC/NoPadding", 192);

    /**
     * Encryption using AES/CBC/NoPadding algorithm with key bitLength 256.
     */
    public static final EncryptionMethod AES256_CBC = new EncryptionMethod("AES-256/CBC", NISTObjectIdentifiers.id_aes256_CBC, "AES/CBC/NoPadding", 256);

    /**
     * Encryption using AES/CFB/NoPadding algorithm with key bitLength 128.
     */
    public static final EncryptionMethod AES128_CFB = new EncryptionMethod("AES-128/CFB", NISTObjectIdentifiers.id_aes128_CFB, "AES/CFB/NoPadding", 128);

    /**
     * Encryption using AES/CFB/NoPadding algorithm with key bitLength 192.
     */
    public static final EncryptionMethod AES192_CFB = new EncryptionMethod("AES-192/CFB", NISTObjectIdentifiers.id_aes192_CFB, "AES/CFB/NoPadding", 192);

    /**
     * Encryption using AES/CFB/NoPadding algorithm with key bitLength 256.
     */
    public static final EncryptionMethod AES256_CFB = new EncryptionMethod("AES-256/CFB", NISTObjectIdentifiers.id_aes256_CFB, "AES/CFB/NoPadding", 256);

    /**
     * Encryption using AES/OFB/NoPadding algorithm with key bitLength 128.
     */
    public static final EncryptionMethod AES128_OFB = new EncryptionMethod("AES-128/OFB", NISTObjectIdentifiers.id_aes128_OFB, "AES/OFB/NoPadding", 128);

    /**
     * Encryption using AES/OFB/NoPadding algorithm with key bitLength 192.
     */
    public static final EncryptionMethod AES192_OFB = new EncryptionMethod("AES-192/OFB", NISTObjectIdentifiers.id_aes192_OFB, "AES/OFB/NoPadding", 192);

    /**
     * Encryption using AES/OFB/NoPadding algorithm with key bitLength 256.
     */
    public static final EncryptionMethod AES256_OFB = new EncryptionMethod("AES-256/OFB", NISTObjectIdentifiers.id_aes256_OFB, "AES/OFB/NoPadding", 256);

    /**
     * Encryption using AES/ECB/PKCS5Padding algorithm with key bitLength 128.
     */
    public static final EncryptionMethod AES128_ECB_PKCS5 = new EncryptionMethod("AES-128/ECB/PKCS5", NISTObjectIdentifiers.id_aes128_ECB, "AES/ECB/PKCS5Padding", 128);

    /**
     * Encryption using AES/ECB/PKCS5Padding algorithm with key bitLength 192.
     */
    public static final EncryptionMethod AES192_ECB_PKCS5 = new EncryptionMethod("AES-128/ECB/PKCS5", NISTObjectIdentifiers.id_aes192_ECB, "AES/ECB/PKCS5Padding", 192);

    /**
     * Encryption using AES/ECB/PKCS5Padding algorithm with key bitLength 256.
     */
    public static final EncryptionMethod AES256_ECB_PKCS5 = new EncryptionMethod("AES-128/ECB/PKCS5", NISTObjectIdentifiers.id_aes256_ECB, "AES/ECB/PKCS5Padding", 256);

    /**
     * Encryption using AES/CBC/PKCS5Padding algorithm with key bitLength 128.
     */
    public static final EncryptionMethod AES128_CBC_PKCS5 = new EncryptionMethod("AES-128/CBC/PKCS5", NISTObjectIdentifiers.id_aes128_CBC, "AES/CBC/PKCS5Padding", 128);

    /**
     * Encryption using AES/CBC/PKCS5Padding algorithm with key bitLength 192.
     */
    public static final EncryptionMethod AES192_CBC_PKCS5 = new EncryptionMethod("AES-192/CBC/PKCS5", NISTObjectIdentifiers.id_aes192_CBC, "AES/CBC/PKCS5Padding", 192);

    /**
     * Encryption using AES/CBC/PKCS5Padding algorithm with key bitLength 256.
     */
    public static final EncryptionMethod AES256_CBC_PKCS5 = new EncryptionMethod("AES-256/CBC/PKCS5", NISTObjectIdentifiers.id_aes256_CBC, "AES/CBC/PKCS5Padding", 256);

    /**
     * Encryption using RC2/CBC/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod RC2_CBC = new EncryptionMethod("RC2/CBC", PKCSObjectIdentifiers.RC2_CBC, "RC2/CBC/PKCS5Padding");

    /**
     * Encryption using RC4/ECB/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod RC4 = new EncryptionMethod("RC4", PKCSObjectIdentifiers.rc4, "RC4/ECB/PKCS5Padding");

    /**
     * Encryption using DES/ECB/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod DES_ECB_PKCS5 = new EncryptionMethod("DES/ECB/PKCS5", OIWObjectIdentifiers.desECB, "DES/ECB/PKCS5Padding", 64);

    /**
     * Encryption using DES/CBC/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod DES_CBC_PKCS5 = new EncryptionMethod("DES/CBC/PKCS5", OIWObjectIdentifiers.desCBC, "DES/CBC/PKCS5Padding", 64);

    /**
     * Encryption using DESede/ECB/NoPadding algorithm.
     */
    public static final EncryptionMethod DESEDE_ECB = new EncryptionMethod("DESede/ECB", OIWObjectIdentifiers.desEDE, "DESede/ECB/NoPadding", 192);

    /**
     * Encryption using DESede/ECB/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod DESEDE_ECB_PKCS5 = new EncryptionMethod("DESede/ECB/PKCS5", OIWObjectIdentifiers.desEDE, "DESede/ECB/PKCS5Padding", 192);

    /**
     * Encryption using DESede/ECB/PKCS7Padding algorithm.
     */
    public static final EncryptionMethod DESEDE_ECB_PKCS7 = new EncryptionMethod("DESede/ECB/PKCS7", OIWObjectIdentifiers.desEDE, "DESede/ECB/PKCS7Padding", 192);

    /**
     * Encryption using DESede/CBC/NoPadding algorithm.
     */
    public static final EncryptionMethod DESEDE_CBC = new EncryptionMethod("DESede/CBC", PKCSObjectIdentifiers.des_EDE3_CBC, "DESede/CBC/NoPadding", 192);

    /**
     * Encryption using DESede/CBC/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod DESEDE_CBC_PKCS5 = new EncryptionMethod("DESede/CBC/PKCS5", PKCSObjectIdentifiers.des_EDE3_CBC, "DESede/CBC/PKCS5Padding", 192);

    /**
     * Encryption using DESede/CBC/PKCS7Padding algorithm.
     */
    public static final EncryptionMethod DESEDE_CBC_PKCS7 = new EncryptionMethod("DESede/CBC/PKCS7", PKCSObjectIdentifiers.des_EDE3_CBC, "DESede/CBC/PKCS7Padding", 192);

    /**
     * Encryption using ECIES algorithm.
     */
    public static final EncryptionMethod ECIES = new EncryptionMethod("ECIES", null, "ECIES", 192);

    /**
     * Encryption using ECIESwithSHA1 algorithm.
     */
    public static final EncryptionMethod ECIES_SHA1 = new EncryptionMethod("ECIESwithSHA1", null, "ECIESwithSHA1", 192);

    /**
     * Encryption using ECIESwithSHA256 algorithm.
     */
    public static final EncryptionMethod ECIES_SHA256 = new EncryptionMethod("ECIESwithSHA256", null, "ECIESwithSHA256", 192);

    /**
     * Encryption using ECIESwithSHA384 algorithm.
     */
    public static final EncryptionMethod ECIES_SHA384 = new EncryptionMethod("ECIESwithSHA384", null, "ECIESwithSHA384", 192);

    /**
     * Encryption using ECIESwithSHA512 algorithm.
     */
    public static final EncryptionMethod ECIES_SHA512 = new EncryptionMethod("ECIESwithSHA512", null, "ECIESwithSHA512", 192);

    /**
     * Encryption using ECIESwithDESedeCBC algorithm.
     */
    public static final EncryptionMethod ECIES_DESEDE_CBC = new EncryptionMethod("ECIESwithDESedeCBC", null, "ECIESwithDESede-CBC", 192);

    /**
     * Encryption using ECIESwithSHA1andDESedeCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA1_DESEDE_CBC = new EncryptionMethod("ECIESwithSHA1andDESedeCBC", null, "ECIESwithSHA1andDESede-CBC", 192);

    /**
     * Encryption using ECIESwithSHA256andDESedeCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA256_DESEDE_CBC = new EncryptionMethod("ECIESwithSHA256andDESedeCBC", null, "ECIESwithSHA256andDESede-CBC", 192);

    /**
     * Encryption using ECIESwithSHA384andDESedeCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA384_DESEDE_CBC = new EncryptionMethod("ECIESwithSHA384andDESedeCBC", null, "ECIESwithSHA384andDESede-CBC", 192);

    /**
     * Encryption using ECIESwithSHA512andDESedeCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA512_DESEDE_CBC = new EncryptionMethod("ECIESwithSHA512andDESedeCBC", null, "ECIESwithSHA512andDESede-CBC", 192);

    /**
     * Encryption using ECIESwithAESCBC algorithm.
     */
    public static final EncryptionMethod ECIES_AES_CBC = new EncryptionMethod("ECIESwithAESCBC", null, "ECIESwithAES-CBC", 192);

    /**
     * Encryption using ECIESwithSHA1andAESCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA1_AES_CBC = new EncryptionMethod("ECIESwithSHA1andAESCBC", null, "ECIESwithSHA1andAES-CBC", 192);

    /**
     * Encryption using ECIESwithSHA256andAESCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA256_AES_CBC = new EncryptionMethod("ECIESwithSHA256andAESCBC", null, "ECIESwithSHA256andAES-CBC", 192);

    /**
     * Encryption using ECIESwithSHA384andAESCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA384_AES_CBC = new EncryptionMethod("ECIESwithSHA384andAESCBC", null, "ECIESwithSHA384andAES-CBC", 192);

    /**
     * Encryption using ECIESwithSHA512andAESCBC algorithm.
     */
    public static final EncryptionMethod ECIES_SHA512_AES_CBC = new EncryptionMethod("ECIESwithSHA512andAESCBC", null, "ECIESwithSHA512andAES-CBC", 192);

    /**
     * Encryption using RSA/None/NoPadding algorithm.
     */
    public static final EncryptionMethod RSA_NONE = new EncryptionMethod("RSA/NONE", PKCSObjectIdentifiers.rsaEncryption, "RSA/None/NoPadding", 2048);

    /**
     * Encryption using RSA/ECB/NoPadding algorithm.
     */
    public static final EncryptionMethod RSA_ECB = new EncryptionMethod("RSA/ECB", PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/NoPadding", 2048);

    /**
     * Encryption using RSA/ECB/PKCS1Padding algorithm.
     */
    public static final EncryptionMethod RSA_ECB_PKCS1 = new EncryptionMethod("RSA/ECB/PKCS1", PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding", 2048);

    /**
     * Encryption using SM2 algorithm.
     */
    public static final EncryptionMethod SM2 = new EncryptionMethod("SM2", GMObjectIdentifiers.sm2encrypt, "SM2", 256);

    /**
     * Encryption using SM4/ECB/NoPadding algorithm.
     */
    public static final EncryptionMethod SM4_ECB = new EncryptionMethod("SM4/ECB", GMObjectIdentifiers.sms4_ecb, "SM4/ECB/NoPadding", 128);

    /**
     * Encryption using SM4/ECB/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod SM4_ECB_PKCS5 = new EncryptionMethod("SM4/ECB/PKCS5", GMObjectIdentifiers.sms4_ecb, "SM4/ECB/PKCS5Padding", 128);
    
    /**
     * Encryption using SM4/ECB/PKCS7Padding algorithm.
     */
    public static final EncryptionMethod SM4_ECB_PKCS7 = new EncryptionMethod("SM4/ECB/PKCS7", GMObjectIdentifiers.sms4_ecb, "SM4/ECB/PKCS7Padding", 128);

    /**
     * Encryption using SM4/CBC/NoPadding algorithm.
     */
    public static final EncryptionMethod SM4_CBC = new EncryptionMethod("SM4/CBC", GMObjectIdentifiers.sms4_cbc, "SM4/CBC/NoPadding", 128);

    /**
     * Encryption using SM4/CBC/PKCS5Padding algorithm.
     */
    public static final EncryptionMethod SM4_CBC_PKCS5 = new EncryptionMethod("SM4/CBC/PKCS5", GMObjectIdentifiers.sms4_cbc, "SM4/CBC/PKCS5Padding", 128);
   
    /**
     * Encryption using SM4/CBC/PKCS7Padding algorithm.
     */
    public static final EncryptionMethod SM4_CBC_PKCS7 = new EncryptionMethod("SM4/CBC/PKCS7", GMObjectIdentifiers.sms4_cbc, "SM4/CBC/PKCS7Padding", 128);

    /**
     * The supported encryption methods.
     */
    public static final Map<String, EncryptionMethod> SUPPORTED_METHODS;

    static {
        Map<String, EncryptionMethod> algs = new LinkedHashMap<>();
        // AES
        algs.put(AES128_ECB.getName(), AES128_ECB);
        algs.put(AES192_ECB.getName(), AES192_ECB);
        algs.put(AES256_ECB.getName(), AES256_ECB);

        algs.put(AES128_CBC.getName(), AES128_CBC);
        algs.put(AES192_CBC.getName(), AES192_CBC);
        algs.put(AES256_CBC.getName(), AES256_CBC);

        algs.put(AES128_CFB.getName(), AES128_CFB);
        algs.put(AES192_CFB.getName(), AES192_CFB);
        algs.put(AES256_CFB.getName(), AES256_CFB);

        algs.put(AES128_OFB.getName(), AES128_OFB);
        algs.put(AES192_OFB.getName(), AES192_OFB);
        algs.put(AES256_OFB.getName(), AES256_OFB);

        algs.put(AES128_ECB_PKCS5.getName(), AES128_ECB_PKCS5);
        algs.put(AES192_ECB_PKCS5.getName(), AES192_ECB_PKCS5);
        algs.put(AES256_ECB_PKCS5.getName(), AES256_ECB_PKCS5);

        algs.put(AES128_CBC_PKCS5.getName(), AES128_CBC_PKCS5);
        algs.put(AES192_CBC_PKCS5.getName(), AES192_CBC_PKCS5);
        algs.put(AES256_CBC_PKCS5.getName(), AES256_CBC_PKCS5);

        // RC2/RC4
        algs.put(RC2_CBC.getName(), RC2_CBC);
        algs.put(RC4.getName(), RC4);

        // DES
        algs.put(DES_ECB_PKCS5.getName(), DES_ECB_PKCS5);
        algs.put(DES_CBC_PKCS5.getName(), DES_CBC_PKCS5);

        // DESede
        algs.put(DESEDE_ECB.getName(), DESEDE_ECB);
        algs.put(DESEDE_ECB_PKCS5.getName(), DESEDE_ECB_PKCS5);
        algs.put(DESEDE_ECB_PKCS7.getName(), DESEDE_ECB_PKCS7);
        algs.put(DESEDE_CBC.getName(), DESEDE_CBC);
        algs.put(DESEDE_CBC_PKCS5.getName(), DESEDE_CBC_PKCS5);
        algs.put(DESEDE_CBC_PKCS7.getName(), DESEDE_CBC_PKCS7);

        // RSA
        algs.put(RSA_NONE.getName(), RSA_NONE);
        algs.put(RSA_ECB.getName(), RSA_ECB);
        algs.put(RSA_ECB_PKCS1.getName(), RSA_ECB_PKCS1);

        // SM2
        algs.put(SM2.getName(), SM2);

        // SM4
        algs.put(SM4_ECB.getName(), SM4_ECB);
        algs.put(SM4_ECB_PKCS5.getName(), SM4_ECB_PKCS5);
        algs.put(SM4_ECB_PKCS7.getName(), SM4_ECB_PKCS7);
        algs.put(SM4_CBC.getName(), SM4_CBC);
        algs.put(SM4_CBC_PKCS5.getName(), SM4_CBC_PKCS5);
        algs.put(SM4_CBC_PKCS7.getName(), SM4_CBC_PKCS7);

        // ECIES
        algs.put(ECIES.getName(), ECIES);
        algs.put(ECIES_SHA1.getName(), ECIES_SHA1);
        algs.put(ECIES_SHA256.getName(), ECIES_SHA256);
        algs.put(ECIES_SHA384.getName(), ECIES_SHA384);
        algs.put(ECIES_SHA512.getName(), ECIES_SHA512);
        algs.put(ECIES_DESEDE_CBC.getName(), ECIES_DESEDE_CBC);
        algs.put(ECIES_SHA1_DESEDE_CBC.getName(), ECIES_SHA1_DESEDE_CBC);
        algs.put(ECIES_SHA256_DESEDE_CBC.getName(), ECIES_SHA256_DESEDE_CBC);
        algs.put(ECIES_SHA384_DESEDE_CBC.getName(), ECIES_SHA384_DESEDE_CBC);
        algs.put(ECIES_SHA512_DESEDE_CBC.getName(), ECIES_SHA512_DESEDE_CBC);
        algs.put(ECIES_AES_CBC.getName(), ECIES_AES_CBC);
        algs.put(ECIES_SHA1_AES_CBC.getName(), ECIES_SHA1_AES_CBC);
        algs.put(ECIES_SHA256_AES_CBC.getName(), ECIES_SHA256_AES_CBC);
        algs.put(ECIES_SHA384_AES_CBC.getName(), ECIES_SHA384_AES_CBC);
        algs.put(ECIES_SHA512_AES_CBC.getName(), ECIES_SHA512_AES_CBC);
        SUPPORTED_METHODS = Collections.unmodifiableMap(algs);
    }

    /**
     * Key algorithm family.
     */
    public static final class Family extends AlgorithmFamily<EncryptionMethod> {

        private static final long serialVersionUID = 1L;

        /**
         * Encryption using AES.
         */
        public static final Family AES = new Family(AES128_ECB, AES192_ECB, AES256_ECB, AES128_CBC, AES192_CBC, AES256_CBC, AES128_CFB, AES192_CFB, AES256_CFB, AES128_OFB,
                AES192_OFB, AES256_OFB, AES128_ECB_PKCS5, AES192_ECB_PKCS5, AES256_ECB_PKCS5, AES128_CBC_PKCS5, AES192_CBC_PKCS5, AES256_CBC_PKCS5);

        /**
         * Encryption using DES.
         */
        public static final Family DES = new Family(DES_ECB_PKCS5, DES_CBC_PKCS5);

        /**
         * Encryption using DESede.
         */
        public static final Family DESEDE = new Family(DESEDE_ECB, DESEDE_ECB_PKCS5, DESEDE_ECB_PKCS7, DESEDE_CBC, DESEDE_CBC_PKCS5, DESEDE_CBC_PKCS7);

        /**
         * Encryption using ECIES.
         */
        public static final Family ECIES = new Family(EncryptionMethod.ECIES, ECIES_SHA1, ECIES_SHA256, ECIES_SHA384, ECIES_SHA512, ECIES_DESEDE_CBC, ECIES_SHA1_DESEDE_CBC, ECIES_SHA256_DESEDE_CBC,
                ECIES_SHA384_DESEDE_CBC, ECIES_SHA512_DESEDE_CBC, ECIES_AES_CBC, ECIES_SHA1_AES_CBC, ECIES_SHA256_AES_CBC, ECIES_SHA384_AES_CBC, ECIES_SHA512_AES_CBC);

        /**
         * Encryption using RSA.
         */
        public static final Family RSA = new Family(RSA_NONE, RSA_ECB, RSA_ECB_PKCS1);

        /**
         * Encryption using SM4.
         */
        public static final Family SM4 = new Family(SM4_ECB, SM4_ECB_PKCS5, SM4_ECB_PKCS7, SM4_CBC, SM4_CBC_PKCS5, SM4_CBC_PKCS7);

        /**
         * Creates a new encryption method family.
         *
         * @param encs The encryption methods of the family. Must not be {@code null}.
         */
        public Family(final EncryptionMethod... encs) {
            super(encs);
        }
    }

    /**
     * Creates a new encryption algorithm name.
     * 
     * @param name the algorithm name. Must not be {@code null}.
     * @param oid the algorithm object identifier. Must not be {@code null}.
     * @param algorithm the algorithm. Must not be {@code null}.
     */
    public EncryptionMethod(final String name, final ASN1ObjectIdentifier oid, final String algorithm) {
        this(name, oid, algorithm, 0);
    }

    /**
     * Creates a new encryption algorithm name.
     * 
     * @param name the algorithm name. Must not be {@code null}.
     * @param oid the algorithm object identifier. Must not be {@code null}.
     * @param algorithm the algorithm. Must not be {@code null}.
     * @param keyLength the key length, zero if not specified.
     */
    public EncryptionMethod(final String name, final ASN1ObjectIdentifier oid, final String algorithm, final int keyLength) {
        super(name, oid);

        this.algorithm = algorithm;
        this.keyLength = keyLength;
    }

    /**
     * Gets encryption algorithm.
     *
     * @return the encryption algorithm
     */
    public String getAlgorithm() {
        return algorithm;
    }

    /**
     * Gets the length of the associated Encryption Key (CEK).
     *
     * @return The encryption key length.
     */
    public int getKeyLength() {
        return keyLength;
    }

    /**
     * Parses a encryption method from the specified string.
     *
     * @param name the encryption method name. Must not be {@code null}.
     * @return the encryption method.
     * @throws NoSuchAlgorithmException the ${@link NoSuchAlgorithmException}
     */
    public static EncryptionMethod find(final String name) throws NoSuchAlgorithmException {
        EncryptionMethod method = SUPPORTED_METHODS.get(name);
        if (method == null) {
            throw new NoSuchAlgorithmException("Unknown encryption method type requested: " + name);
        }

        return method;
    }

    /**
     * Find a encryption algorithm from the specified string.
     *
     * @param algorithmId the encryption algorithm id. Must not be {@code null}.
     * @return the cipher algorithm.
     * @throws NoSuchAlgorithmException the ${@link NoSuchAlgorithmException}
     */
    public static EncryptionMethod findByAlgorithmId(final AlgorithmIdentifier algorithmId) throws NoSuchAlgorithmException {
        final ASN1ObjectIdentifier oid = algorithmId.getAlgorithm();
        return SUPPORTED_METHODS.values().stream().filter(algorithm -> Objects.nonNull(algorithm.getOID()) && Objects.equals(algorithm.getOID(), oid)).findFirst()
                .orElseThrow(() -> new NoSuchAlgorithmException("Unknown cipher algorithm type requested: " + oid));
    }
}
